package com.zzg.mybatis.generator.plugins;

import com.alibaba.fastjson.JSON;
import com.zzg.mybatis.generator.model.UITableQueryColumnVO;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.generator.api.*;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Document;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.config.PropertyRegistry;
import org.mybatis.generator.internal.NullProgressCallback;
import org.mybatis.generator.internal.rules.ConditionalModelRules;

import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 石超
 * @version v1.0.0
 */
public class SpringMybatisPlugin extends PluginAdapter {
    private String javaTypeDO;

    private String javaTypeQueryDO;

    private boolean isQuery = false;

    private List<UITableQueryColumnVO> queryColumnS;

    private boolean sinceTheGrowth = false;

    private boolean overrideXML = false;

    private String clientPath;
    private String modelPath;
    private String servicePath;
    private String controllerPath;

    private String clientPackage;
    private String modelPackage;
    private String servicePackage;
    private String controllerPackage;
    private boolean isMybatisPlus = false;

    public static final String QUERY = "query";
    public static final String SINCE_THE_GROWTH = "since_the_growth";
    public static final String OVERRIDE_XML = "override_xml";

    public static final String CLIENT_PATH = "client_path";
    public static final String MODEL_PATH = "model_path";
    public static final String SERVICE_PATH = "service_path";
    public static final String CONTROLLER_PATH = "controller_path";


    public static final String CLIENT_PACKAGE = "client_package";
    public static final String MODEL_PACKAGE = "model_package";
    public static final String SERVICE_PACKAGE = "service_package";
    public static final String CONTROLLER_PACKAGE = "controller_package";
    public static final String IS_MYBATIS_PLUS = "is_mybatis_plus";

    private Document document;

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public void initialized(IntrospectedTable introspectedTable) {
        sinceTheGrowth = Boolean.valueOf(properties.getProperty(SINCE_THE_GROWTH, "false"));
        String queryJson = properties.getProperty(QUERY);

        overrideXML = Boolean.valueOf(properties.getProperty(OVERRIDE_XML, "false"));
        modelPath = properties.getProperty(MODEL_PATH, introspectedTable.getContext().getJavaClientGeneratorConfiguration().getTargetPackage());
        clientPath = properties.getProperty(CLIENT_PATH, introspectedTable.getContext().getJavaClientGeneratorConfiguration().getTargetPackage());

        servicePath = properties.getProperty(SERVICE_PATH);
        controllerPath = properties.getProperty(CONTROLLER_PATH);


        clientPackage = properties.getProperty(CLIENT_PACKAGE);
        modelPackage = properties.getProperty(MODEL_PACKAGE);
        servicePackage = properties.getProperty(SERVICE_PACKAGE);
        controllerPackage = properties.getProperty(CONTROLLER_PACKAGE);

        isMybatisPlus = BooleanUtils.toBooleanObject(properties.getProperty(IS_MYBATIS_PLUS));

        introspectedTable.setMyBatis3JavaMapperType(modelPackage + ".dao." + introspectedTable.getTableConfiguration().getDomainObjectName() + "DAO");
        introspectedTable.setMyBatis3XmlMapperPackage(modelPackage + ".dao");


        if (queryJson != null) {
            try {
                queryColumnS = JSON.parseArray(queryJson, UITableQueryColumnVO.class);
                isQuery = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        javaTypeDO = modelPackage + ".domain." + introspectedTable.getTableConfiguration().getDomainObjectName();
        introspectedTable.setBaseRecordType(javaTypeDO + "DO");

        introspectedTable.setRules(new ConditionalModelRules(introspectedTable) {
            @Override
            public boolean generateInsert() {
                return false;
            }

            @Override
            public boolean generateUpdateByPrimaryKeyWithoutBLOBs() {
                return false;
            }

            @Override
            public boolean generateUpdateByPrimaryKeyWithBLOBs() {
                return false;
            }

            @Override
            public boolean generateRecordWithBLOBsClass() {
                return !isMybatisPlus && super.generateRecordWithBLOBsClass();
            }

            @Override
            public boolean generateInsertSelective() {
                return !isMybatisPlus && super.generateInsertSelective();
            }

            @Override
            public boolean generateUpdateByPrimaryKeySelective() {
                return !isMybatisPlus && super.generateUpdateByPrimaryKeySelective();
            }

            @Override
            public boolean generateDeleteByPrimaryKey() {
                return !isMybatisPlus && super.generateDeleteByPrimaryKey();
            }

            @Override
            public boolean generateBaseResultMap() {
                return !isMybatisPlus && super.generateBaseResultMap();
            }

            @Override
            public boolean generateResultMapWithBLOBs() {
                return !isMybatisPlus && super.generateResultMapWithBLOBs();
            }

            @Override
            public boolean generateSelectByPrimaryKey() {
                return !isMybatisPlus && super.generateSelectByPrimaryKey();
            }

            @Override
            public boolean generateUpdateByExampleWithoutBLOBs() {
                return !isMybatisPlus && super.generateUpdateByExampleWithoutBLOBs();
            }

            @Override
            public boolean generateBaseColumnList() {
                return !isMybatisPlus && super.generateBaseColumnList();
            }

            @Override
            public boolean generateBlobColumnList() {
                return !isMybatisPlus && super.generateBlobColumnList();
            }
        });

        introspectedTable.setDeleteByPrimaryKeyStatementId("deleteById");
        introspectedTable.setUpdateByPrimaryKeySelectiveStatementId("updateById");
        introspectedTable.setInsertSelectiveStatementId("insert");
        introspectedTable.setSelectByPrimaryKeyStatementId("selectById");
    }

    @Override
    public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType DO = new FullyQualifiedJavaType(javaTypeDO + "DO");
        interfaze.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Repository"));
        interfaze.addImportedType(DO);

        FullyQualifiedJavaType fullyQualifiedJavaType;

        if (isMybatisPlus) {
            fullyQualifiedJavaType = new FullyQualifiedJavaType("com.baomidou.mybatisplus.core.mapper.BaseMapper<" + DO.getFullyQualifiedName() + ">");
            interfaze.addImportedType(new FullyQualifiedJavaType("com.baomidou.mybatisplus.core.mapper.BaseMapper"));
        } else if (isQuery) {
            FullyQualifiedJavaType queryDTO = new FullyQualifiedJavaType(javaTypeDO + "QueryDTO");
            fullyQualifiedJavaType = new FullyQualifiedJavaType("org.summerframework.core.base.dao.BaseQueryDAO<" + DO.getFullyQualifiedName() + "," + queryDTO.getFullyQualifiedName() + ">");
            interfaze.addImportedType(new FullyQualifiedJavaType("org.summerframework.core.base.dao.BaseQueryDAO"));
            interfaze.addImportedType(queryDTO);
        } else {
            interfaze.addImportedType(new FullyQualifiedJavaType("org.summerframework.core.base.dao.BaseDAO"));
            fullyQualifiedJavaType = new FullyQualifiedJavaType("org.summerframework.core.base.dao.BaseDAO<" + DO.getFullyQualifiedName() + ">");
        }

        interfaze.addAnnotation("@Repository");
        interfaze.addSuperInterface(fullyQualifiedJavaType);
        interfaze.getMethods().clear();

        return super.clientGenerated(interfaze, topLevelClass, introspectedTable);
    }

    @Override
    public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) {
        this.document = document;
        return super.sqlMapDocumentGenerated(document, introspectedTable);
    }

    @Override
    public boolean sqlMapInsertSelectiveElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        if (sinceTheGrowth) {
            element.addAttribute(new Attribute("keyProperty", StringUtils.join(introspectedTable.getPrimaryKeyColumns().stream().map(IntrospectedColumn::getActualColumnName).toArray(), ",")));
            element.addAttribute(new Attribute("useGeneratedKeys", "true"));
        }

        return !isMybatisPlus && super.sqlMapInsertElementGenerated(element, introspectedTable);
    }

    @Override
    public boolean modelFieldGenerated(Field field, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
//        if (introspectedColumn.getJdbcType() == Types.TINYINT) {
//            FullyQualifiedJavaType integer = new FullyQualifiedJavaType("java.lang.Integer");
//            field.setType(integer);
//        }
//
//        if (StringUtils.equals(field.getType().getShortName(), "Date")) {
//            if (introspectedColumn.getJdbcType() == Types.DATE) {
//                FullyQualifiedJavaType integer = new FullyQualifiedJavaType("java.time.LocalDate");
//                field.setType(integer);
//            } else if (introspectedColumn.getJdbcType() == Types.TIME) {
//                FullyQualifiedJavaType integer = new FullyQualifiedJavaType("java.time.LocalTime");
//                field.setType(integer);
//            } else if (introspectedColumn.getJdbcType() == Types.TIMESTAMP) {
//                FullyQualifiedJavaType integer = new FullyQualifiedJavaType("java.time.LocalDateTime");
//                field.setType(integer);
//            }
//        }
        return super.modelFieldGenerated(field, topLevelClass, introspectedColumn, introspectedTable, modelClassType);
    }

    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        introspectedTable.setBaseRecordType(introspectedTable.getBaseRecordType().replaceAll("DO$", ""));
        List<GeneratedJavaFile> generatedJavaFiles = new ArrayList<>();
        QueryGenerator queryGenerator = new QueryGenerator();
        queryGenerator.setContext(introspectedTable.getContext());
        queryGenerator.setIntrospectedTable(introspectedTable);
        queryGenerator.setWarnings(new ArrayList<>());
        queryGenerator.setProgressCallback(new NullProgressCallback());
        queryGenerator.setQueryColumnVOS(queryColumnS);
        queryGenerator.setMybatisPlus(isMybatisPlus);

        queryGenerator.setClientPackage(clientPackage);
        queryGenerator.setModelPackage(modelPackage);
        queryGenerator.setServicePackage(servicePackage);
        queryGenerator.setControllerPackage(controllerPackage);

        queryGenerator.setDocument(document);

        List<CompilationUnit> compilationUnits = queryGenerator.getCompilationUnits();
        for (CompilationUnit compilationUnit : compilationUnits) {
            GeneratedJavaFile gjf;

            if (compilationUnit.getType().getFullyQualifiedName().contains(servicePackage) && compilationUnit.getType().getFullyQualifiedName().endsWith("Impl")) {
                gjf = new GeneratedJavaFile(compilationUnit,
                        servicePath,
                        context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
                        context.getJavaFormatter());
            } else if (compilationUnit.getType().getFullyQualifiedName().contains(clientPackage)) {
                gjf = new GeneratedJavaFile(compilationUnit,
                        clientPath,
                        context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
                        context.getJavaFormatter());
            } else if (compilationUnit.getType().getFullyQualifiedName().contains(modelPackage)) {
                gjf = new GeneratedJavaFile(compilationUnit,
                        modelPath,
                        context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
                        context.getJavaFormatter());
            } else if (compilationUnit.getType().getFullyQualifiedName().contains(controllerPackage)) {
                gjf = new GeneratedJavaFile(compilationUnit,
                        controllerPath,
                        context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
                        context.getJavaFormatter());
            } else {
                gjf = new GeneratedJavaFile(compilationUnit,
                        context.getJavaModelGeneratorConfiguration().getTargetProject(),
                        context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING),
                        context.getJavaFormatter());
            }

            generatedJavaFiles.add(gjf);
        }

        return generatedJavaFiles;
    }

    @Override
    public boolean sqlMapUpdateByPrimaryKeySelectiveElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        return !isMybatisPlus && super.sqlMapUpdateByPrimaryKeySelectiveElementGenerated(element, introspectedTable);
    }

    @Override
    public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
        sqlMap.setMergeable(overrideXML);
        return super.sqlMapGenerated(sqlMap, introspectedTable);
    }

    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        //实体类的import
        topLevelClass.addImportedType("lombok.Data");
        topLevelClass.addImportedType("lombok.EqualsAndHashCode");

        //实体类的注解
        topLevelClass.addAnnotation("@Data");
        topLevelClass.addAnnotation("@EqualsAndHashCode(callSuper = true)");
        return true;
    }

    /**
     * 不生成getter
     */
    @Override
    public boolean modelGetterMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
        return false;
    }

    /**
     * 不生成setter
     */
    @Override
    public boolean modelSetterMethodGenerated(Method method, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn, IntrospectedTable introspectedTable, ModelClassType modelClassType) {
        return false;
    }
}
